/* 
 * File:   Window.h
 * Author: johannes
 *
 * Created on December 19, 2013, 11:32 AM
 */

#ifndef WINDOW_H
#define	WINDOW_H

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <string>
#include <vector>
#include <utility>

#include "Utility.h"
#include "angelscript.h"
//#include "Renderer.h"

namespace GUL {
    namespace System {
        namespace Graphic {

            class Renderer;
            ;
            class Window {
            private:
                SDL_Window* m_Display;
                Renderer* m_RenderManager;
                
                unsigned m_referenceCount;

                bool m_valid;
                bool m_Texture;
                bool m_borderd;
                
                SDL_Event e;

            protected:

                /**
                 *  \brief Get the SDL surface associated with the window.
                 *
                 *  \return The window's framebuffer surface, or NULL on error.
                 *
                 *  A new surface will be created with the optimal format for the window,
                 *  if necessary. This surface will be freed when the window is destroyed.
                 *
                 *  \note You may not combine this with 3D or the rendering API on this window.
                 *
                 *  \sa SDL_UpdateWindowSurface()
                 *  \sa SDL_UpdateWindowSurfaceRects()
                 */
                SDL_Surface* Surface();



            public:
                Window(const std::string& Name, IPair Pos, IPair Boundary, Uint32 flags);
                
                Window(const std::string& Name, int x,int y,int w,int h,Uint32 flags);

                Window() {
                };
                virtual ~Window();
                
                static void AddScriptEngine(asIScriptEngine* engine) ;

                void InitSDLRenderSupport();
                bool IsSDLRenderSupportEnabled();
                void ShutdownSDLRenderSupport();
                Renderer& getRenderer();
                
                
                void AddRef(){this->m_referenceCount++;}
                void ReleaseRef(){this->m_referenceCount--;};

                /**
                 *  \brief Get the numeric ID of a window, for logging purposes.
                 */
                Uint32 ID();

                /**
                 *  \brief Get the window flags.
                 */
                Uint32 Flags();

                /**
                 *  \brief Get the title of a window, in UTF-8 format.
                 *
                 *  \sa SDL_GetWindowTitle()
                 */
                std::string getTitle();

                /**
                 * 
                 * \brief Set the title of a window
                 * 
                 * \sa SDL_SetWindowTitle();
                 * 
                 * @return 
                 */
                void setTitle(const std::string&);

                /**
                 *  \brief Get the position of a window.
                 *
                 *  \return UPair -> first = x; second = y
                 *
                 *  \sa SDL_SetWindowPosition()
                 */
                UPair Position();

                /**
                 *  \brief Get the size of a window's client area.
                 *
                 *  \return UPair.first = width , second = height;
                 *
                 *  \sa SDL_SetWindowSize()
                 */
                UPair& Size();

                /**
                 *  \brief Set the size of a window's client area.
                 *  \param UPair.first = width , second = height;
                 *
                 *  \sa SDL_SetWindowSize()
                 *  \sa SDL_GetWindowSize()
                 */
                void Size(UPair);

                /**
                 *  \brief Get the minimum size of a window's client area.
                 * 
                 *  \return UPair.first = width , second = height;
                 * 
                 *  \sa SDL_GetWindowMaximumSize()
                 *  \sa SDL_SetWindowMinimumSize()
                 */
                UPair MinimumSize();

                /**
                 *  \brief Get the maximum size of a window's client area.
                 *
                 *  \return UPair.first = width , second = height;
                 *
                 *  \sa SDL_GetWindowMinimumSize()
                 *  \sa SDL_SetWindowMaximumSize()
                 */
                UPair MaximumSize();

                /**
                 *  \brief Set the minimum size of a window's client area.
                 *
                 *  \param window    The window to set a new minimum size.
                 *  \param min_w     The minimum width of the window, must be >0
                 *  \param min_h     The minimum height of the window, must be >0
                 *
                 *  \note You can't change the minimum size of a fullscreen window, it
                 *        automatically matches the size of the display mode.
                 *
                 *  \sa SDL_GetWindowMinimumSize()
                 *  \sa SDL_SetWindowMaximumSize()
                 */
                void MinimumSize(UPair);

                /**
                 *  \brief Set the maximum size of a window's client area.
                 *
                 *  \param window    The window to set a new maximum size.
                 *  \param max_w     The maximum width of the window, must be >0
                 *  \param max_h     The maximum height of the window, must be >0
                 *
                 *  \note You can't change the maximum size of a fullscreen window, it
                 *        automatically matches the size of the display mode.
                 *
                 *  \sa SDL_GetWindowMaximumSize()
                 *  \sa SDL_SetWindowMinimumSize()
                 */
                void MaximumSize(UPair);

                /**
                 *  \brief Show a window.
                 *
                 *  \sa SDL_HideWindow()
                 *  \sa SDL_ShowWindow()
                 */
                void Show();

                /**
                 *  \brief Hide a window.
                 *
                 *  \sa SDL_ShowWindow()
                 *  \sa SDL_HideWindow()
                 */
                void Hide();

                /**
                 *  \brief Raise a window above other windows and set the input focus.
                 */
                void Rise();

                /**
                 *  \brief Set the border state of a window.
                 *
                 *  This will add or remove the window's SDL_WINDOW_BORDERLESS flag and
                 *  add or remove the border from the actual window. This is a no-op if the
                 *  window's border already matches the requested state.
                 *
                 *  \param window The window of which to change the border state.
                 *  \param bordered SDL_FALSE to remove border, SDL_TRUE to add border.
                 *
                 *  \note You can't change the border state of a fullscreen window.
                 *
                 *  \sa SDL_GetWindowFlags()
                 */
                void Bordered();

                /**
                 *  \brief Make a window as large as possible.
                 *
                 *  \sa SDL_RestoreWindow()
                 */
                void Maximize();

                /**
                 *  \brief Minimize a window to an iconic representation.
                 *
                 *  \sa SDL_RestoreWindow()
                 */
                void Minimize();

                /**
                 *  \brief Restore the size and position of a minimized or maximized window.
                 *
                 *  \sa SDL_MaximizeWindow()
                 *  \sa SDL_MinimizeWindow()
                 */
                void Restore();

                /**
                 *  \brief Set a window's fullscreen state.
                 *
                 *  \return 0 on success, or -1 if setting the display mode failed.
                 *
                 *  \sa SDL_SetWindowDisplayMode()
                 *  \sa SDL_GetWindowDisplayMode()
                 */
                void Fullscreen();
                //        void                    Windowed();

                /**
                 *  \brief Set a window's input grab mode.
                 *
                 *  \param window The window for which the input grab mode should be set.
                 *  \param grabbed This is SDL_TRUE to grab input, and SDL_FALSE to release input.
                 *
                 *  \sa SDL_GetWindowGrab()
                 */
                SDL_bool SetGrab(SDL_bool);

                /**
                 *  \brief Get a window's input grab mode.
                 *
                 *  \return This returns SDL_TRUE if input is grabbed, and SDL_FALSE otherwise.
                 *
                 *  \sa SDL_SetWindowGrab()
                 */
                SDL_bool Grab();

                /**
                 *  \brief Returns whether the screensaver is currently enabled (default on).
                 *
                 *  \sa SDL_EnableScreenSaver()
                 *  \sa SDL_DisableScreenSaver()
                 */
                SDL_bool IsScreenSaverEnabled();

                /**
                 *  \brief Allow the screen to be blanked by a screensaver
                 *
                 *  \sa SDL_IsScreenSaverEnabled()
                 *  \sa SDL_DisableScreenSaver()
                 */
                void EnableScreenSaver();

                /**
                 *  \brief Prevent the screen from being blanked by a screensaver
                 *
                 *  \sa SDL_IsScreenSaverEnabled()
                 *  \sa SDL_EnableScreenSaver()
                 */
                void DisableScreenSaver();

                /**
                 *  \brief Retrieve the data pointer associated with a window.
                 *
                 *  \param window   The window to query.
                 *  \param name     The name of the pointer.
                 *
                 *  \return The value associated with 'name'
                 *
                 *  \sa SDL_SetWindowData()
                 */
                void* Data();

                /**
                 *  \brief Associate an arbitrary named pointer with a window.
                 *
                 *  \param window   The window to associate with the pointer.
                 *  \param name     The name of the pointer.
                 *  \param userdata The associated pointer.
                 *
                 *  \return The previous value associated with 'name'
                 *
                 *  \note The name is case-sensitive.
                 *
                 *  \sa SDL_GetWindowData()
                 */
                void Data(void*);


                bool IsValid();
                void SetValid(bool);

                /**
                 *  \brief Get the brightness (gamma correction) for a window.
                 *
                 *  \return The last brightness value passed to SDL_SetWindowBrightness()
                 *
                 *  \sa SDL_SetWindowBrightness()
                 */
                float Brightness();

                /**
                 *  \brief Set the brightness (gamma correction) for a window.
                 *
                 *  \return 0 on success, or -1 if setting the brightness isn't supported.
                 *
                 *  \sa SDL_GetWindowBrightness()
                 *  \sa SDL_SetWindowGammaRamp()
                 */
                void Brightness(float);

                /**
                 *  \brief Get the gamma ramp for a window.
                 *
                 *  \param window The window from which the gamma ramp should be queried.
                 *  \param red   A pointer to a 256 element array of 16-bit quantities to hold
                 *               the translation table for the red channel, or NULL.
                 *  \param green A pointer to a 256 element array of 16-bit quantities to hold
                 *               the translation table for the green channel, or NULL.
                 *  \param blue  A pointer to a 256 element array of 16-bit quantities to hold
                 *               the translation table for the blue channel, or NULL.
                 *
                 *  \return 0 on success, or -1 if gamma ramps are unsupported.
                 *
                 *  \sa SDL_SetWindowGammaRamp()
                 */
                std::vector<Uint16*>& GetGammaRamp(Uint16*r,Uint16*g,Uint16*b);

                /**
                 *  \brief Set the gamma ramp for a window.
                 *
                 *  \param window The window for which the gamma ramp should be set.
                 *  \param red The translation table for the red channel, or NULL.
                 *  \param green The translation table for the green channel, or NULL.
                 *  \param blue The translation table for the blue channel, or NULL.
                 *
                 *  \return 0 on success, or -1 if gamma ramps are unsupported.
                 *
                 *  Set the gamma translation table for the red, green, and blue channels
                 *  of the video hardware.  Each table is an array of 256 16-bit quantities,
                 *  representing a mapping between the input and output for that channel.
                 *  The input is the index into the array, and the output is the 16-bit
                 *  gamma value at that index, scaled to the output color precision.
                 *
                 *  \sa SDL_GetWindowGammaRamp()
                 */
                void SetGammaRamp(Uint16*r,Uint16*g,Uint16*b);

                /**
                 *  \brief Clear the actual Window into white
                 * 
                 *  Use a pointer to the window's surface and fill it all in white 
                 * 
                 *  \sa SDL_FillRect(SDL_Surface * dst, const SDL_Rect * rect, Uint32 color)
                 */
                void Clean();

                /**
                 *  \brief Copy the window surface to the screen.
                 *
                 *  \return 0 on success, or -1 on error.
                 *
                 *  \sa SDL_GetWindowSurface()
                 *  \sa SDL_UpdateWindowSurfaceRects()
                 */
                void Update();

                void HandleInput();

                ///TODO: Move operator to source and implement check if Display != 0
                /// is.

                inline operator SDL_Window* () {
                    return this->m_Display;
                };

                inline operator SDL_Surface*() {
                    return this->Surface();
                }
                Renderer& operator()();
                void operator()(Texture&, SDL_Rect* src, SDL_Rect* dst);
                Texture& operator()(const std::string&);
                friend class WindowException;
            };
            
            void WindowCtor(void* mem);
            
            Window* FactoryWindow(const std::string& Name, int x,int y,int w,int h);

        }
    }

}

#endif	/* WINDOW_H */

